package com.nulldev.util.web.HttpClient.clients.fuzzer;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.WritableByteChannel;
import java.util.List;
import java.util.Map;

import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.internal.backport.concurrency9.Maps;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.HttpClient.HttpResponse;
import com.nulldev.util.web.HttpClient.ResponseFormat;

public class FuzzedHttpRepsonse<T> implements HttpResponse<T> {

	private static final Logger log = LoggerFactory.getLoggerDT(true);

	public static final int FUZZER_ERROR_TYPE_4xx_RESPONSE = 1;
	public static final int FUZZER_ERROR_FAKE_STREAM_FAILURE = 2;

	private final ResponseFormat format;
	private final String url;
	private final int errorType;

	public FuzzedHttpRepsonse(final String url, final ResponseFormat format) {
		this.format = format;
		this.url = url;
		this.errorType = 2;
	}

	private void _log() {
		log.info("[FUZZ] URL: " + this.url);
		log.info("[FUZZ] errorMode: " + this.errorType);
	}

	@Override
	public void close() throws Exception {
		log.info("close() called.");
		this._log();
	}

	@Override
	public void writeTo(final OutputStream os) throws IOException {
		log.info("writeTo(OutputStream) called.");
	}

	@Override
	public void writeTo(OutputStream os, final int preferredBufferSize) throws IOException {
		log.info("writeTo(OutputStream, int) called.");
	}

	@Override
	public void writeTo(final WritableByteChannel os) throws IOException {
		log.info("writeTo(WritableByteChannel) called.");
	}

	@Override
	public int status() {
		log.info("status() called.");
		if (this.errorType == FUZZER_ERROR_TYPE_4xx_RESPONSE) {
			return RandomUtil.randInt(400, 405);
		} else {
			return 200;
		}
	}

	@Override
	public long length() {
		log.info("length() called.");
		if (this.errorType == FUZZER_ERROR_FAKE_STREAM_FAILURE) {
			return 1024 * 1024 * 1024;
		} else {
			return -1;
		}
	}

	@Override
	public boolean successful() {
		log.info("successful() called.");
		if (this.errorType == FUZZER_ERROR_FAKE_STREAM_FAILURE) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public ResponseFormat format() {
		log.info("format() called.");
		return this.format;
	}

	@Override
	public T data() {
		return this.data(false);
	}

	@Override
	public T data(final boolean avoidDecoding) {
		log.info("data() called.");
		if (this.errorType == FUZZER_ERROR_FAKE_STREAM_FAILURE) {
			throw new RuntimeException(new IOException("stream closed"));
		} else {
			return null;
		}
	}

	@Override
	public T dataThenClose() {
		return this.dataThenClose(false);
	}

	@Override
	public T dataThenClose(final boolean avoidDecoding) {
		log.info("dataThenClosed(boolean) called.");
		this._log();
		if (this.errorType == FUZZER_ERROR_FAKE_STREAM_FAILURE) {
			throw new RuntimeException(new IOException("stream closed"));
		} else {
			return null;
		}
	}

	@Override
	public Map<String, List<String>> headers() {
		log.info("headers() called.");
		return Maps.of();
	}

	@Override
	public String[] cookies() {
		log.info("cookies() called.");
		return new String[0];
	}

	@Override
	public void adaptiveClose() throws Exception {
		log.info("adaptiveClose() called.");
		this._log();
	}

}
